import frappe
from frappe.model.document import Document


class StaffUser(Document):
    """保留类定义，方便需要时在其它地方直接 import 使用。

    注意：对于 custom DocType，Frappe 默认不会自动调用类方法，
    实际的事件处理请看本文件底部的 doc_event handler。
    """
    pass


def staff_user_after_insert(doc, method=None):
    """DocType 事件：在创建 Staff User 后触发。"""
    create_or_sync_system_user(doc)


def staff_user_on_update(doc, method=None):
    """DocType 事件：在保存/更新 Staff User 时触发。"""
    create_or_sync_system_user(doc)


def create_or_sync_system_user(doc: "StaffUser"):
    """根据 Staff User 记录创建 / 绑定系统 User，并将 user.name 写回 system_user 字段。

    规则：
    - 若 system_user 已存在且对应 User 仍存在，则优先使用该 User
    - 否则按 email 查找已存在的非系统用户；若没有则创建新 User
    - 为 User 分配 shop 相关角色（支持多角色）
    - 若创建者不是系统管理员，则复制创建者的 User Permission 到新用户
    """
    if not doc.email:
        return

    # 1) 查找或创建 User
    user = None

    # 1.1 若已关联系统用户且存在，则优先使用
    linked_user = getattr(doc, "system_user", None)
    if linked_user and frappe.db.exists("User", linked_user):
        user = frappe.get_doc("User", linked_user)
    else:
        # 1.2 否则按 email 查找已存在的 User
        user_name = frappe.db.exists("User", {"email": doc.email})
        if user_name:
            user = frappe.get_doc("User", user_name)

    # 1.3 若找到的 User 拥有系统级权限，则不允许绑定
    if user:
        user_roles = set(frappe.get_roles(user.name))
        if user.name == "Administrator" or "System Manager" in user_roles:
            raise frappe.ValidationError(
                "该邮箱已绑定系统管理员账号，不能作为 Staff User 使用，请更换邮箱。"
            )

    # 1.4 如未找到合适的 User，则创建新用户
    if not user:
        language = getattr(doc, "language", None) or None
        time_zone = getattr(doc, "time_zone", None) or None

        first_name = doc.full_name or (doc.email.split("@")[0] if "@" in doc.email else doc.email)
        user = frappe.get_doc(
            {
                "doctype": "User",
                "email": doc.email,
                "first_name": first_name,
                "enabled": 1 if doc.enabled else 0,
                "send_welcome_email": 1 if doc.send_welcome_email else 0,
                "language": language,
                "time_zone": time_zone,
            }
        )
        user.insert(ignore_permissions=True)

    # 2) 同步 User 基本信息（语言等）
    changed = False
    language = getattr(doc, "language", None) or None
    if language and user.language != language:
        user.language = language
        changed = True

    if hasattr(doc, "time_zone"):
        time_zone = getattr(doc, "time_zone", None) or None
        if time_zone and getattr(user, "time_zone", None) != time_zone:
            user.time_zone = time_zone
            changed = True

    # 3) 分配角色（支持多个 Shop 角色），并与 Staff User 中的配置保持同步（仅管理自定义角色）
    role_names = set()
    for row in getattr(doc, "roles", []) or []:
        role = getattr(row, "role", None)
        if role:
            role_names.add(role)

    if role_names:
        custom_roles = set(
            frappe.get_all("Role", filters={"is_custom": 1}, pluck="name") or []
        )

        new_role_rows = []
        for r in user.get("roles", []):
            # 保留非自定义角色；自定义角色稍后用 Staff User 的配置重建
            if r.role not in custom_roles:
                new_role_rows.append(r)

        # 按当前 Staff User 中的自定义角色追加
        for role_name in sorted(role_names):
            if role_name not in custom_roles:
                continue
            new_role_rows.append(frappe._dict({"role": role_name}))

        new_role_set = {r.role for r in new_role_rows if getattr(r, "role", None)}
        old_role_set = {r.role for r in user.get("roles", [])}
        if new_role_set != old_role_set:
            user.set("roles", new_role_rows)
            changed = True

    # 保存 User（如有变更）
    if changed:
        user.flags.ignore_mandatory = True
        user.save(ignore_permissions=True)

    # 3) 复制创建者的 User Permission（非系统管理员）
    creator = getattr(doc, "owner", None) or frappe.session.user
    if creator and creator not in ("Administrator", "Guest"):
        creator_doc = frappe.get_doc("User", creator)
        if not creator_doc.has_role("System Manager"):
            perms = frappe.get_all(
                "User Permission",
                filters={"user": creator},
                fields=[
                    "allow",
                    "for_value",
                    "apply_to_all_doctypes",
                    "applicable_for",
                    "hide_descendants",
                ],
            )
            for p in perms:
                exists = frappe.db.exists(
                    "User Permission",
                    {"user": user.name, "allow": p["allow"], "for_value": p["for_value"]},
                )
                if exists:
                    continue
                up = frappe.get_doc(
                    {
                        "doctype": "User Permission",
                        "user": user.name,
                        "allow": p["allow"],
                        "for_value": p["for_value"],
                        "apply_to_all_doctypes": p.get("apply_to_all_doctypes") or 0,
                        "applicable_for": p.get("applicable_for"),
                        "hide_descendants": p.get("hide_descendants") or 0,
                    }
                )
                up.insert(ignore_permissions=True)

    # 4) 回写到 Staff User 的隐藏字段
    if user.name != getattr(doc, "system_user", None):
        frappe.db.set_value(doc.doctype, doc.name, "system_user", user.name, update_modified=False)


